#include "dlmlists.h"
#include <iostream>

using namespace std;

list<lock> locks;
list<child_data> children;
list<lock_waiter> waiters;
map<pid_t, timer_descriptor*> activeTimers;

errorCode addLock(struct lock l)
{
    errorCode error = tryLock(l);

    if ( error == DLM_ERR_OK )
    {
        locks.push_back(l);
    }
    return error;
}

errorCode tryLock(struct lock l)
{
    errorCode error = DLM_ERR_OK;

    if ( l.lock_type < DLM_ERR_OK || l.lock_type >= DLM_ERR_COUNT )
    {
        error = DLM_ERR_NO_SUCH_LOCK;
    }

    //Sprawdzenie czy dany proces nie ma już blokady na tym zasobie.
    if ( error == DLM_ERR_OK )
    {
        for (list<lock>::iterator it=locks.begin(); it != locks.end(); ++it)
        {
            if ( it->resource_id == l.resource_id )
            {
                if ( it->owner.pid == l.owner.pid )
                {
                    error = DLM_ERR_ALREADY_LOCKED;
                    break;
                }
            }
        }
    }

    //Sprawdzenie czy nie ma blokad "ważniejszych" od testowanej.
    if ( error == DLM_ERR_OK )
    {
        for (list<lock>::iterator it=locks.begin(); it != locks.end(); ++it)
        {
            if ( it->resource_id == l.resource_id )
            {
                if ( it->lock_type == DLM_LOCK_EX || l.lock_type == DLM_LOCK_EX )
                {
                    error = DLM_ERR_CANNOT_LOCK;
                    break;
                }
                else if ( it->lock_type == DLM_LOCK_PW && l.lock_type != DLM_LOCK_CR )
                {
                    error = DLM_ERR_CANNOT_LOCK;
                    break;
                }
                else if ( it->lock_type == DLM_LOCK_PR && l.lock_type != DLM_LOCK_PR && l.lock_type != DLM_LOCK_CR )
                {
                    error = DLM_ERR_CANNOT_LOCK;
                    break;
                }
                else if ( it->lock_type == DLM_LOCK_CW && l.lock_type >= DLM_LOCK_PR )
                {
                    error = DLM_ERR_CANNOT_LOCK;
                    break;
                }
            }
        }
    }
    return error;
}
errorCode removeLock(pid_t pid, int resource_id)
{
    errorCode error = DLM_ERR_NO_SUCH_LOCK;

    for (list<lock>::iterator it=locks.begin(); it != locks.end(); ++it)
    {
        if ( it->owner.pid == pid && it->resource_id == resource_id )
        {
            locks.erase(it);
            error = DLM_ERR_OK;
            break;
        }
    }
    return error;
}


list<struct lock> procLocks(pid_t pid)
{
    list<struct lock> retval;
    for (list<lock>::iterator it=locks.begin(); it != locks.end(); ++it)
    {
        if ( it->owner.pid == pid )
        {
            lock l = *it;
            retval.push_back(l);
        }
    }
    return retval;
}

set<pid_t> procsWithLocks()
{
    set<pid_t> retval;
    for (list<lock>::iterator it=locks.begin(); it != locks.end(); ++it)
    {
        pid_t i = it->owner.pid;
        retval.insert(i);

    }
    return retval;
}

void printAllLocks()
{
    cout << "Aktualnie sa " << locks.size() << " blokady. " << endl;
    cout << "-----------------------Blokady:--------------------------" << endl;
    for (list<lock>::iterator it=locks.begin(); it != locks.end(); ++it)
    {
        cout << "Numer zasobu: " << it->resource_id << ",typ blokady: " << it->lock_type << ", wlasciciel blokady: " << it->owner.pid << endl;
    }
    cout << "Liczba oczekujących: " << waiters.size() << endl;
    cout << "-----------------------Oczekujący------------------------" << endl;
    for (list<lock_waiter>::iterator it = waiters.begin(); it != waiters.end(); ++it)
    {
    	cout << "Nr zasobu: " << it->resource_lock.resource_id << " wlasciciel: " << it->resource_lock.owner.pid << " timeout: " << it->timeout << endl;
    }
    cout << "---------------------------------------------------------" << endl;
}

void printProcLocks(pid_t pid)
{
    list<struct lock> l = procLocks(pid);

    cout << "Proces " << pid << " posiada " << l.size() << "blokad" << endl;
    cout << "-----------------------Blokady:--------------------------" << endl;
    for (list<lock>::iterator it=l.begin(); it != l.end(); ++it)
    {
        cout << "Numer zasobu: " << it->resource_id << ",typ blokady: " << it->lock_type << endl;
    }
    cout << "---------------------------------------------------------" << endl;
}

void printProcsWithLocks()
{
    set<pid_t> s = procsWithLocks();
    cout << "Aktualnie " << s.size() << " procesow posiada blokady." << endl;
    cout << "-----------------------Procesy:--------------------------" << endl;
    for (set<pid_t>::iterator it=s.begin(); it != s.end(); ++it)
    {
        cout << "PID: " << (*it) << endl;
    }
    cout << "---------------------------------------------------------" << endl;

}

bool addChild(child_data child)
{
    bool retval = true;
    for (list<child_data>::iterator it=children.begin(); it != children.end(); ++it)
    {
        if ( it->pid == child.pid )
        {
            retval = false;
            break;
        }
    }

    if ( retval )
    {
        children.push_back(child);
    }
    return retval;
}

bool removeChild(pid_t pid)
{
    list<child_data>::iterator it = children.begin();
    for (; it != children.end(); ++it)
    {
        if ( it->pid == pid )
        {
            children.erase(it);
            return true;
        }
    }
    return false;
}

child_data getChild(pid_t pid) {
    child_data retval;
    retval.pid = 0;

	list<child_data>::iterator it = children.begin();
    for (; it != children.end(); ++it)
    {
        if ( it->pid == pid )
        {
            retval = (*it);
        }
    }
    return retval;
}

bool addWaiter(lock_waiter waiter)
{
    bool retval = true;
    for (list<lock_waiter>::iterator it=waiters.begin(); it != waiters.end(); ++it)
    {
        if ( it->resource_lock.owner.pid == waiter.resource_lock.owner.pid )
        {
            retval = false;
            break;
        }
    }

    if ( retval )
    {
        waiters.push_back(waiter);
    }
    return retval;
}

lock_waiter getFirstWaiter(int resource_id)
{
    lock_waiter retval;
    retval.resource_lock.owner.pid = 0;

    list<lock_waiter>::iterator it = waiters.begin();
    for (; it != waiters.end(); ++it)
    {
        if ( it->resource_lock.resource_id == resource_id )
        {
            retval = *it;
            waiters.erase(it);
            break;
        }
    }

    printf("ok?");
    return retval;
}

bool removeWaiter(pid_t pid)
{
    list<lock_waiter>::iterator it = waiters.begin();
    for (; it != waiters.end(); ++it)
    {
        if ( it->resource_lock.owner.pid == pid )
        {
            waiters.erase(it);
            return true;
        }
    }
    return false;
}

void addTimerDescriptor(pid_t waiter_pid, timer_descriptor *timer_desc)
{
	activeTimers.insert(std::make_pair(waiter_pid, timer_desc));
}

timer_descriptor *peekTimerDescriptor(pid_t waiter_pid)
{
	timer_descriptor *result = 0;
	map<pid_t, timer_descriptor*>::iterator it = activeTimers.find(waiter_pid);
	if (it != activeTimers.end())
	{
		result = it->second;
		activeTimers.erase(it);
	}

	return result;
}

void removeTimerDescriptor(pid_t waiter_pid)
{
	map<pid_t, timer_descriptor*>::iterator it = activeTimers.find(waiter_pid);
	if (it != activeTimers.end())
		activeTimers.erase(it);
}

